﻿# -*- coding: UTF-8 -*-
import Pyro4
import sqlite3
import vue as v
import socket
import sys
import action


sys.path.append("../Errors")
sys.path.append("../Generation-Galaxie")
sys.path.append("../Objets-Ali")
import Galaxie
import Planete
import entite
import joueur
import OrionErrors

class Modele():
    def __init__(self, parent):
        self.worldSize = 1000
        self.widthEspaceJeu = 800
        self.heightEspaceJeu = 600
        
        self.currentFrame = 0
        self.parent = parent
        self.unitesDejaRepositionnees = [] #Liste des unites deja repositionnees ce tour ci
        self.unitesAVerifierCollisions = [] #Liste des unites avec lesquelles il faudra verifier les positions
        self.listePrix = ListePrix()
        self.joueur = None
                
    def debutPartie(self):
        self.joueurs = self.parent.creeListeJoueurs()
        #Jolan voici ce qui fesait en sorte que le code n etait pas fonctionnel
        self.joueur.remplirListeStatus(self.joueurs)
        self.galaxie=Galaxie.Galaxie(self.parent.getSeed(),self.joueurs, self.worldSize)
        #self.planete.genererEmplacementDepart(self.joueurs) 
        
    def getMyIp(self):
        return socket.gethostbyname(socket.gethostname())
        
class ListePrix():
#   Liste de tous les prix pour chacune des unite et batiments du jeux
    def __init__(self):
#       les ressources dans un ordre precis soit le meme que l'affichage
#       donc : "fer", "petrole","fertilite","gaz","titane","cuivre","or","argent"
#       Si une ressource n'est pas necessaire on met un 0.
        self.tank = [100,0,0,0,0,0,0,0]
        self.vaisseau = [100,0,0,0,0,0,0,0]
        self.explorateur = [0,0,0,0,0,0,0,0]
        self.transporteur = [0,0,0,0,0,0,0,0]
        self.fregate = [0,0,0,0,0,0,0,0]
        self.colonisateur = [0,0,0,0,0,0,0,0]
        self.croiseur = [0,0,0,0,0,0,0,0]
        self.usine = [100,0,0,0,0,0,0,0]
        self.tour = [100,0,0,0,0,0,0,0]
        self.caserne = [0,0,0,0,0,0,0,0]
        self.stationSpatiale = [100,0,0,0,0,0,0,0]
        self.mine = [0,0,0,0,0,0,0,0] #gratuit sinon on ne pourrait jamais rien construire .. On pourrait mettre des ressources de bases, à voir
        self.ferme = [100,0,0,0,0,0,0,0]
        self.habitation = [100,0,0,0,0,0,0,0]
        self.centreRecherche = [100,0,0,0,0,0,0,0]
        self.centreCommunication = [100,0,0,0,0,0,0,0]
                
    def getPrixUnite(self,unite):
#       unite est le nom en string du véhicule dont
#       dont on veut avoir le prix. Renvoie la bonne liste de ressources pour l'unite.
        if unite == "Tank":
            return self.tank
        elif unite == "Vaisseau":
            return self.vaisseau
        elif unite == "Explorateur":
            return self.explorateur
        elif unite == "Transporteur":
            return self.transporteur
        elif unite == "Fregate":
            return self.fregate
        elif unite == "Colonisateur":
            return self.colonisateur
        elif unite == "Croiseur":
            return self.croiseur
        
    def getPrixBatiment(self,batiment):
#       comme pour les unites mais c'est un batiment cette fois.
        if batiment == "Caserne":
            return self.caserne
        elif batiment == "Usine":
            return self.usine
        elif batiment == "Tour":
            return self.tour
        elif batiment == "Station Spatiale":
            return self.stationSpatiale
        elif batiment == "Mine":
            return self.mine
        elif batiment == "Ferme":
            return self.ferme
        elif batiment == "Habitation":
            return self.habitation
        elif batiment == "Centre de Recherche":
            return self.centreRecherche
        elif batiment == "Centre de Communication":
            return self.centreCommunication

  
class Controleur():
    def __init__(self):
        self.modele = Modele(self)
        self.vue = v.Vue(self)
        self.actionsAEnvoyer = [] #Actions a envoyer au serveur
        self.actionsAFaire = [] #Actions dans la "queue" a faire a chaque tour de boucle
        self.vue.master.mainloop()
        
    def validerLogin(self, adresseServeur, nomJoueur, motDePasse):
        try:
            self.serveur = Pyro4.core.Proxy("PYRO:serveur@" + adresseServeur + ":54440") #Creation de la connexion sur le serveur
            connexionReussie = self.serveur.validerLogin(nomJoueur, motDePasse) #Va verifier sur le serveur si les informations de login sont valides
        except Pyro4.errors.CommunicationError:
            print("Le serveur a l'adresse " + adresseServeur + " est introuvable.")
        #except OrionErrors.DoubleConnexionError as dce:
        #    print("test")
        except:
            print("Utilisateur deja connecte a l'aide d'un autre client")
        else:
            if connexionReussie:
                self.modele.joueur = joueur.Joueur(nomJoueur,"blue", self)
                if self.serveur.isPartieDemarree():
                    self.initialisationsPartieDemarree()
                else:
                    self.vue.afficherSalle()
                    self.ecouteurDebutPartie()
            else:
                self.message = "Usager " + nomJoueur + " inexistant, mot de passe invalide, ou utilisateur connecte"
    
    def initialisationsPartieDemarree(self):
        self.currentFrame = self.serveur.getMaxFrame()
        self.modele.debutPartie()
        self.vue.afficherPartie()
        self.frameIncrement()
        
    def ecouteurDebutPartie(self):
        if self.serveur.isPartieDemarree():
            self.initialisationsPartieDemarree()
        else:
            self.vue.miseAJours()
            self.vue.raffraichirChat()
            self.vue.master.after(50, self.ecouteurDebutPartie)
    def frameIncrement(self):
        if self.serveur.getGeneralFreeze() == self.currentFrame and self.serveur.getGeneralFreeze() != 0:
            print("Freeze jusqu'a " + str(self.serveur.getGeneralFreeze()) + " presentement a " + str(self.currentFrame))
        else:
            #Afin de ne pas perdre d'actions pendant que les donnees sont transmises au serveur
            copie_ActionsAEnvoyer = self.actionsAEnvoyer
            self.actionsAEnvoyer = []
            
            self.currentFrame = self.currentFrame + 1
            self.modele.galaxie.collecterRessourcesSurChaquePlanete(self.currentFrame,self.modele.joueur.nom)
            try:      
                actionsRecues = self.serveur.receptionFrame(self.modele.joueur.nom, self.currentFrame, copie_ActionsAEnvoyer)
                
                for action in actionsRecues:
                    action.parent = self.modele
                    self.ajouterOuModifierAction(self.actionsAFaire, action)                        
                
                for i in reversed(range(len(self.actionsAFaire))):
                    if self.actionsAFaire[i].frame == self.currentFrame:
                        action = self.actionsAFaire[i]
                        del self.actionsAFaire[i]
                        action.effectuerAction()
                #self.repositionnerUnites()
                
            except ValueError as ve:
                print("Joueur deconnecte")
         
        self.vue.frameHaut.redraw()
        self.vue.frameDiplomatie.redraw()
        self.vue.canvas.redraw()
        self.vue.hud.redraw()
        self.vue.menuNavigation.insertionElements()
        self.vue.master.after(50, self.frameIncrement)
    
    def repositionnerUnites(self):
        copieUnitesAVerifierCollisions = self.modele.unitesAVerifierCollisions
        self.modele.unitesAVerifierCollisions = []
        
        for infoUnite in copieUnitesAVerifierCollisions:
            unite = infoUnite[0]
            
            systemeId = infoUnite[1]
            planeteId = infoUnite[2]
            groupeUnitesEnCollision = []
            
            if unite not in self.modele.unitesDejaRepositionnees and not unite.enDeplacement and not unite.enRepositionnement:
                groupeUnitesEnCollision.append(unite)
                
                for entite in self.modele.galaxie.listeSystemes[systemeId].listePlanetes[planeteId].listeEntites:
                    if entite not in self.modele.unitesDejaRepositionnees and entite.id != unite.id and entite.detecterCollision(unite) and not entite.enDeplacement and not entite.enRepositionnement:
                        groupeUnitesEnCollision.append(entite)
                
                if len(groupeUnitesEnCollision) > 1:
                    if len(groupeUnitesEnCollision) % 2 == 0: 
                        sousGroupe1 = len(groupeUnitesEnCollision) / 2
                    else:
                         sousGroupe1 = (len(groupeUnitesEnCollision) + 1) / 2
                         
                    for i in range(len(groupeUnitesEnCollision)):
                        if i == 0:
                            groupeUnitesEnCollision[i].targetPosX = groupeUnitesEnCollision[i].posX
                            groupeUnitesEnCollision[i].targetPosY = groupeUnitesEnCollision[i].posY
                            groupeUnitesEnCollision[i].enRepositionnement = True
                        elif i < sousGroupe1:
                            groupeUnitesEnCollision[i].targetPosX = groupeUnitesEnCollision[0].posX - i * groupeUnitesEnCollision[0].dimX - 1
                            groupeUnitesEnCollision[i].targetPosY = groupeUnitesEnCollision[i].posY
                            groupeUnitesEnCollision[i].enRepositionnement = True
                        else:
                            groupeUnitesEnCollision[i].targetPosX = groupeUnitesEnCollision[0].posX + (i - sousGroupe1 + 1) * groupeUnitesEnCollision[0].dimX + 1
                            groupeUnitesEnCollision[i].targetPosY = groupeUnitesEnCollision[i].posY
                            groupeUnitesEnCollision[i].enRepositionnement = True
                        
                        resultatDeplacement = groupeUnitesEnCollision[i].deplacementParUnTic(groupeUnitesEnCollision[i].targetPosX, groupeUnitesEnCollision[i].targetPosY, 0)
                        
                        if resultatDeplacement:
                            groupeUnitesEnCollision[i].posX = groupeUnitesEnCollision[i].posX + resultatDeplacement[4] - groupeUnitesEnCollision[i].posX
                            groupeUnitesEnCollision[i].posY = groupeUnitesEnCollision[i].posY + resultatDeplacement[5] - groupeUnitesEnCollision[i].posY
                            self.modele.unitesAVerifierCollisions.append([groupeUnitesEnCollision[i], systemeId, planeteId])
                            
                        self.modele.unitesDejaRepositionnees.append(groupeUnitesEnCollision[i])
            
            elif unite.enRepositionnement:
                resultatDeplacement = unite.deplacementParUnTic(unite.targetPosX, unite.targetPosY, 0)
                
                if resultatDeplacement:
                    unite.posX = unite.posX + resultatDeplacement[4] - unite.posX
                    unite.posY = unite.posY + resultatDeplacement[5] - unite.posY
                    self.modele.unitesAVerifierCollisions.append([unite, systemeId, planeteId])
                            
                self.modele.unitesDejaRepositionnees.append(unite)                
                                        
        self.modele.unitesDejaRepositionnees = []
        
    def creerNouveauCompte(self, nomUtilisateur, motDePasse):
        try: #Verifie si l'usager a bien ete ajoute
            self.serveur.creerNouveauCompte(nomUtilisateur, motDePasse)
        except sqlite3.IntegrityError: #L'exception avertit le client que le nom d'utilisateur existe deja
            print("Impossible d'ajouter l'utilisateur " + nomUtilisateur + ", car il est deja present dans la base de donnees")
            
    def creeListeJoueurs(self):
        return self.serveur.retournerListeJoueurs()
    
    def retirerDeLaListe(self):
        try:
            self.serveur.retirerDeLaListe(self.getJoueurCourant())
        except:
            print("Il n'y avait pas de joueur a enlever de la liste de joueurs")
        finally:
            self.vue.master.destroy()
            sys.exit()
            raise SystemExit
        
    def insererTexteChat(self, message):
        self.serveur.insererTexteChat(self.getJoueurCourant(), message)
            
    def creeListeChat(self):
        return self.serveur.creeListeChat()
    
    def retournerListeCouleursJoueurs(self):
        return self.serveur.retournerListeCouleurs()
    
    def getIpServeur(self):
        return self.serveur.getIp()
    
    def getCreateurPartie(self):
        return self.serveur.getCreateurPartie()
    
    def envoiDebutPartie(self):
        self.serveur.debuterPartie()
        
    def getSeed(self):
        return self.serveur.getSeed()
    
    def getMyIp(self):
        return self.modele.getMyIp()
    
    def getJoueurCourant(self):
        return self.modele.joueur.nom
    
    def isJoueurCourantCreateurPartie(self):
        return self.modele.joueur.isCreateurPartie
    
    def ajouterOuModifierAction(self, listeActions, actionAAjouter):
        indexActionTrouvee = -1
        
        for indexAction in range(len(listeActions)): #Boucle qui verifie s'il y a deja une action avec le meme entiteId, si oui, on remplace son action par la nouvelle action
            if listeActions[indexAction].entiteId == actionAAjouter.entiteId:
                indexActionTrouvee = indexAction
                break
            
        if indexActionTrouvee > -1:
            listeActions[indexActionTrouvee] = actionAAjouter
        else: #Si l'entite n'avait pas deja d'action, on ajoute l'action qu'il a a faire
            listeActions.append(actionAAjouter)

    def sortirUnitePlanete(self, listeUnites, systemeId, planeteId):
        for unite in listeUnites:
            monAction = action.ActionSortirPlanete(None, 0, unite[1], systemeId, planeteId)
        
            self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction)

    def entrerUnitePlanete(self, listeUnites, systemeId, planeteId):
        for unite in listeUnites:
            monAction = action.ActionEntrerPlanete(None, 0, unite[1], systemeId, planeteId)
        
            self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction)
     
    def deplacerUnite(self, entiteId, planeteId, systemeId, xFinal, yFinal, zFinal):
        monAction = action.ActionDeplacement(None, 0, entiteId, systemeId, planeteId, xFinal, yFinal, zFinal) #Creation de l'action correspondant au mouvement de l'unite

        self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction) #On utilise la methode pour ajouter ou modifier la liste contenant les actions a faire
    
    def attaquerUnite(self, entiteId, targetId, planeteId, systemeId):
        monAction = action.ActionAttaquer(None, 0, entiteId, targetId, systemeId, planeteId) #Creation de l'action correspondant a l'attaque d'une unite
        
        self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction) #On utilise la methode pour ajouter ou modifier la liste contenant les actions a faire
        
    def ressourcesDispo(self,ressourcesJoueur,typeConstruction,uniteOuBatiment):
        self.prixConstruction = ""
        
        if uniteOuBatiment == "batiment":
            self.prixConstruction = self.modele.listePrix.getPrixBatiment(typeConstruction)
        elif uniteOuBatiment == "unite":
            self.prixConstruction = self.modele.listePrix.getPrixUnite(typeConstruction)
        
        for i in range(len(ressourcesJoueur)):
            if ressourcesJoueur[i-1] < self.prixConstruction[i-1]:
                return False
        return True

    def creerBatiment(self,systemeId, planeteId,typeBatiment, posX, posY, posZ):
        IDBatiment=1000+self.serveur.genererId()
        monAction = action.ActionCreerBatiment(None, 0, IDBatiment, systemeId, planeteId, typeBatiment,posX, posY, posZ, self.modele.joueur.nom)
        
        self.prixBatiment = self.modele.listePrix.getPrixBatiment(typeBatiment)
        for i in range(len(self.prixBatiment)):
            if self.prixBatiment[i-1] > 0:
                self.modele.galaxie.trouverSystemeParID(systemeId).ressourcesJoueur[i-1] = self.modele.galaxie.trouverSystemeParID(systemeId).ressourcesJoueur[i-1] - self.prixBatiment[i-1]
        
        self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction) #On utilise la methode pour ajouter ou modifier la liste contenant les actions a faire
       
       
       
    def creerUnite(self,systemeId, planeteId,typeUnite, posX, posY, posZ,rallyX, rallyY, rallyZ):
        IDUnite=1000+self.serveur.genererId()
        
        monAction = action.ActionCreerUnite(None, 0,IDUnite, systemeId, planeteId,posX, posY, posZ,rallyX, rallyY, rallyZ,typeUnite, self.modele.joueur.nom)
        
        self.prixUnite = self.modele.listePrix.getPrixUnite(typeUnite)
        for i in range(len(self.prixUnite)):
            if self.prixUnite[i-1] > 0:
                self.modele.galaxie.trouverSystemeParID(systemeId).ressourcesJoueur[i-1] = self.modele.galaxie.trouverSystemeParID(systemeId).ressourcesJoueur[i-1] - self.prixUnite[i-1]
        
        self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction)
        
    def dessinerAttaque(self, uniteAttaquante, uniteAttaquee):
        coordUniteAttaquante = (uniteAttaquante.posX, uniteAttaquante.posY)
        coordUniteAttaquee = (uniteAttaquee.posX, uniteAttaquee.posY)
        
        self.vue.dessinerAttaque(coordUniteAttaquante, coordUniteAttaquee)
    #Verifie le status et le modifie si necessaire.  Utilise lors des attaques
    def verifierStatus(self,nomJoueur):
        if(not self.modele.joueur.isStatus(nomJoueur,self.modele.joueur.nom,"allie")):
                        if(self.modele.joueur.isStatus(nomJoueur,self.modele.joueur.nom,"neutre")):
                            # remplacer ... faut Envoyer action pour changer status
                            self.modifierStatus(nomJoueur,self.modele.joueur.nom,"ennemi")
                        return True
        else:
            return False
        
    def modifierStatus(self,nomJoueur1,nomJoueur2, newStatus):
        monAction = action.ActionChangerStatus(None, self.currentFrame+1, None, None, None, nomJoueur1, nomJoueur2, newStatus)
        self.ajouterOuModifierAction(self.actionsAEnvoyer, monAction)
        
    def getHeightEspaceJeu(self):
        return self.modele.heightEspaceJeu
    
    def getWidthEspaceJeu(self):
        return self.modele.widthEspaceJeu
             
if __name__ == "__main__":
    controleur = Controleur()
    
